import datetime

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

class ObolAgentManager(models.Manager):

    def get_or_create(self, agent_object, agent_name):
        try:
            ct = ContentType.objects.get_for_model(type(agent_object))
            agent = ObolAgent.objects.get(content_type=ct, object_id=agent_object.id)
        except ObolAgent.DoesNotExist:
            agent = ObolAgent(name=agent_name, content_object=agent_object)
            agent.save()
        return agent

class ObolAgent(models.Model):
    
    """A person or organization that has rights 
    to give or take ObolEvents.
    
    ObolAgent is not a standalone model.  It exists to provide 
    obol behavior for objects from other apps, such as User.
    
    The preferred method for getting an ObolAgent for an app object is
    ObolAgent.objects.get_or_create(agent_object, agent_object_name).
    That method prevents duplicate agents for the same app object.
    
    """
    
    name = models.CharField(_('name'), max_length=32)
    content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
    object_id = models.PositiveIntegerField(_('object id'))
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    objects = ObolAgentManager()
    
    def __unicode__(self):
        return self.name
    
    def total_obols(self):
        return sum(event.obols for event in self.taken_events.all())
    
    def obols_from_group(self, group):
        ct = ContentType.objects.get_for_model(type(group))
        return sum(event.obols for event in self.taken_events.filter(group_type=ct, group_id=group.id))


class ObolToGiveManager(models.Manager):

    def get_or_create(self, agent, group_object):
        try:
            ct = ContentType.objects.get_for_model(type(group_object))
            otg = ObolsToGive.objects.get(agent=agent, group_type=ct, group_id=group_object.id)
        except ObolsToGive.DoesNotExist:
            otg = ObolsToGive(agent=agent, group=group_object)
            otg.obols = 25
            otg.save()
        return otg
    

class ObolsToGive(models.Model):
    
    """The number of obols that an ObolAgent has available to give 
    in a particular ObolGroup at the current time.
    
    The algorithm for assigning the number of obols
    remains to be determined.
    
    """
       
    agent = models.ForeignKey(ObolAgent, verbose_name=_('agent'))
    group_type = models.ForeignKey(ContentType, verbose_name=_('group type'))
    group_id = models.PositiveIntegerField(_('group id'))
    group = generic.GenericForeignKey('group_type', 'group_id')
    obols = models.IntegerField(_('obols'), default=0)
    objects = ObolToGiveManager()
    
    def __unicode__(self):
        return " ".join([
            self.agent.name, 
            str(self.group), 
            str(self.obols)])




class ObolEventManager(models.Manager):
   
    def events_for_group(self, group):
        ct = ContentType.objects.get_for_model(type(group))
        return ObolEvent.objects.filter(group_type=ct, group_id=group.id)
    
    def events_for_agent_in_group(self, agent, group):
        ct = ContentType.objects.get_for_model(type(group))
        return ObolEvent.objects.filter(group_type=ct, group_id=group.id, to_whom=agent).order_by('-when')
    
    def agent_scores_for_group(self, group):
        # todo: test
        agent_score_dict = {}
        for event in self.events_for_group(group):
            agent_score_dict.setdefault(event.to_whom, 0)
            agent_score_dict[event.to_whom] += event.obols
        agent_score_list = []
        for key in agent_score_dict:
            agent_score = {'agent': key, 'obols': agent_score_dict[key]}
            agent_score_list.append(agent_score)
        agent_score_list.sort(lambda x, y: cmp(y['obols'], x['obols']))
        return agent_score_list
        #agent_score_list.sort(lambda x, y: cmp(y[1], x[1]))
        #score_string_list = []
        #for item in agent_score_list:
        #    score_string_list.append(' '.join([item[0].name, str(item[1])]))
        #return score_string_list
        
    
class ObolEvent(models.Model):
    
    
    """Giving some obols from one ObolAgent to another.
    
    The obols to be given could be for what_resource,
    which may hold a GenericForeignKey to an external app object;
    or they could be informal with only a what_for string  
    and maybe a url.
    Probably a single event form will expose 
    either informal or formal resources, 
    but probably not both in the same form.
    
    """
    
    group_type = models.ForeignKey(ContentType, blank=True, null=True, related_name='group_events', verbose_name=_('group type'))
    group_id = models.PositiveIntegerField(_('group id'), blank=True, null=True)
    group = generic.GenericForeignKey('group_type', 'group_id')
    from_whom = models.ForeignKey(ObolAgent, related_name='given_events', verbose_name=_('from whom'))
    to_whom = models.ForeignKey(ObolAgent, related_name='taken_events', verbose_name=_('to_whom'))
    what_resource_type = models.ForeignKey(ContentType, blank=True, null=True, related_name='resource_events', verbose_name=_('resource type'))
    what_resource_id = models.PositiveIntegerField(_('resource id'), blank=True, null=True)
    what_resource = generic.GenericForeignKey('what_resource_type', 'what_resource_id')
    what_for = models.CharField(_('what for'), max_length=64)
    web_resource = models.URLField(_('web resource'), blank=True, null=True)
    obols = models.IntegerField(_('obols'), default=1)
    when = models.DateTimeField(_('when'), default=datetime.datetime.now())
    objects = ObolEventManager()
    
    def __unicode__(self):
        if self.what_resource:
            what_description = str(self.what_resource)
        else:
            what_description = self.what_for
        return " ".join([
            'from:', self.from_whom.name,
            'to:', self.to_whom.name,
            str(self.obols), 'obols for:', 
            what_description,
            'on:', self.when.strftime('%Y-%m-%d')])
        
    def save(self):
        
        """ Subtract event obols from from_whom's ObolsToGive """
        
        super(ObolEvent, self).save()
        if self.group:         
            otg = ObolsToGive.objects.get_or_create(self.from_whom, self.group)
            if otg:
                otg.obols -= self.obols
                otg.save()

